using System;
using System.Diagnostics;

namespace Dida
{
	public interface IStateMachine
	{
	}

	public interface IStateMachine<GlueTrait> : IStateMachine
	{
		void Start(IStateMachineUser<GlueTrait> user);
		void Stop();

		GlueTrait GetCurStateGlue();
	}

	public enum StateActivatedReason
	{
		GoToState,
		Pushed,
		OtherStatePopped,
	}

	public enum StateDeactivatedReason
	{
		GoToOtherState,
		Popped,
		OtherStatePushed,
	}

	public interface IState<GlueTrait>
		where GlueTrait : StateGlueTrait
	{
		StateTrait<GlueTrait> GetTrait(Type type);
		T GetTrait<T>()
			where T : StateTrait<GlueTrait>;
	}

	public interface IStateMachineUser<GlueTrait>
	{
		void InitializeGlue(GlueTrait glue);
	}

	public abstract class StateTrait<GlueTrait> : Trait
		where GlueTrait : StateGlueTrait
	{
		/// <summary>
		/// <para>
		/// This is a trait method that's is resolved in the BackendTrait, and is
		/// used internally to get the state machine this state belongs to.
		/// </para>
		/// <para>
		/// It's not adviced to use this method directly. Use the 
		/// <see cref = "StateMachine"/> property instead.
		/// </para>
		/// </summary>
		/// <returns>The StateMachine this state belongs to.</returns>
		[TraitDeclareMethod(TraitMethodAcceptance.SingleImplementation)]
		public abstract IStateMachine GetStateMachineImpl();

		/// <summary>
		/// Gets the state machine this state belongs to.
		/// </summary>
		public IStateMachine<GlueTrait> StateMachine
		{
			get { return (IStateMachine<GlueTrait>)GetStateMachineImpl(); }
		}
	}

	public interface IStateTransitionGlue<GlueTrait>
		where GlueTrait : StateGlueTrait
	{
		IState<GlueTrait> PrepareTransition();
		void DoTransition();
	}

	public abstract class StateTransition<GlueTrait> : StateTrait<GlueTrait>
		where GlueTrait : StateGlueTrait
	{
		IStateTransitionGlue<GlueTrait> transitionGlue;

		public void Initialize(IStateTransitionGlue<GlueTrait> transitionGlue)
		{
			this.transitionGlue = transitionGlue;
		}

		protected IState<GlueTrait> PrepareTransition()
		{
			return transitionGlue.PrepareTransition();
		}

		protected void DoTransition()
		{
			transitionGlue.DoTransition();
		}
	}
}